using System;
using System.Linq;
using System.Collections.Generic;

namespace HPM.Console
{
	class Program
	{
		#region ProgramTitle
		private const string outputtitle = @"
.                                                    .
|_ . .._ _  _.._   ._ . .._.._  _  __ _   ._ _  _. _.|_ *._  _
[ )(_|[ | )(_][ )  [_)(_|[  [_)(_)_) (/,  [ | )(_](_.[ )|[ )(/,
                   |        |
v1.0
ax@humanpurposemachine.com
";
			#endregion
		
		public static string[] commands = {"new simulation","show simulations", "choose simulation","show variables", "update variable","start","stop","remove simulation","quit","help"}; //TODO: "load simulations"
		public static string[] shortcommands = {"ns","ss", "cs", "sv", "uv", "rs", "q", "h", "newsim","showsims","choosesim","showvars","updatevar","removesim"}; //make sure long commands have unique abbrevs
		public static string[] exitstrings = {"exit","quit","close","q","q!"};
		
		public static int ChosenSimIndex { get; set; }
		
		public static List<Core.Simulation> Simulations { get; set; } //TODO: give user option to load/serialize on get/set
		
		//TODO: pass through app args to loop and then wait and process for one liners
		public static void Main (string[] args)
		{			
			try {
				ChosenSimIndex = 1; //initially set to outside range to require user choice. 0 may be possible index.
				Program.Simulations = new List<Core.Simulation> (); //TODO: allow save/load from disk/cloud interactivity
				cout (outputtitle);
				cout ("Type help for list of commands.");
				loopprogram ();
			} catch (Exception ex) {
				handleerror (ex);
			}
		}
		
		private static void loopprogram ()
		{
			try {
				newline ();
				cout ("Enter command:");
				waitandprocesscommand ();
				loopprogram ();
			} catch (Exception ex) {
				handleerror (ex);
				loopprogram ();
			}
		}
		
		//TODO: update with regex and group conditional logic
		//TODO: allow multiple commands seperated by semicolon in single line and pass through from app args
		private static void waitandprocesscommand ()
		{
			string input = cin ();
			checkexit (input);
			input = input.Trim ().Replace ("ulation", "").Replace ("iable", "").ToLower ();
			string[] args = new string[2]; //set to max number of args or resize on retrieval/reset
			
			//regex will be nice improvement. point in case
			string type1 = input.Replace (" ", ""); //type0
			string type2 = type1;
			if ((input.Contains (" ")) && (!input.Contains ("help"))) {
				if (input.Split (' ').Count () >= 1) {
					//overwrite values if multiple params
					type1 = input.Split (' ') [0];
					type2 = (input.Split (' ') [0] + input.Split (' ') [1]).Replace (" ", "");
				}
			}
			
			string match = "";
			if (commands.Concat (shortcommands).Contains (type1)) {
				match = type1;
			} else if (commands.Concat (shortcommands).Contains (type2)) {
				match = type2;
			}
			
			if (!string.IsNullOrEmpty (match)) {
				switch (match) {
					case ("start"):
					Command.start ();
					break;
					case ("stop"):
					Command.stop ();
					break;
				case ("showfunctions"):
					Command.showfunctions ();
					break;
				case ("sf"):
					Command.showfunctions ();
					break;
				case ("call"):
					args = input.Replace ("call", "").Split (' ');
					if (args.Count () == 2) {
						Command.call (args [1]);
					} else {
						Program.newline ();
						cout ("Unrecognized arguments or arguments not specified. Arguments may not contain spaces.");
						Program.newline ();
					}
					break;
				case ("removesim"):
					args = input.Replace ("removesim", "").Replace ("remove sim", "").Split (' ');
					if (args.Count () == 2) {
						Command.removesimulation (Convert.ToInt32 (args [1]));
					} else {
						Program.newline ();
						cout ("Unrecognized arguments or arguments not specified. Arguments may not contain spaces.");
						Program.newline ();
					}
					break;
				case ("rs"):
					args = input.Replace ("rs", "").Replace ("r s", "").Split (' ');
					if (args.Count () == 2) {
						Command.removesimulation (Convert.ToInt32 (args [1]));
					} else {
						Program.newline ();
						cout ("Unrecognized arguments or arguments not specified. Arguments may not contain spaces.");
						Program.newline ();
					}
					break;
				case ("updatevar"):
					args = input.Replace ("updatevar", "").Replace ("update var", "").Split (' ');
					if (args.Count () == 3) {
						Command.updatevariable (args [1], Convert.ToInt32 (args [2]));
					} else {
						Program.newline ();
						cout ("Unrecognized arguments or arguments not specified. Arguments may not contain spaces.");
						Program.newline ();
					}
					break;
				case ("uv"):
					args = input.Replace ("uv", "").Replace ("u v", "").Split (' ');
					if (args.Count () == 3) {
						Command.updatevariable (args [1], Convert.ToInt32 (args [2])); //TODO: exception handling. currently will throw up to loopprogram
					} else {
						Program.newline ();
						cout ("Unrecognized arguments or arguments not specified. Arguments may not contain spaces.");
						Program.newline ();
					}
					break;
				case ("showvars"):
					Command.showvariables ();
					break;
				case ("sv"):
					Command.showvariables ();
					break;
				case ("choosesim"):
					args = input.Replace ("choosesim", "").Replace ("choose sim", "").Split (' ');
					if (args.Count () == 2) {
						Command.choosesimulation (Convert.ToInt32 (args [1]));
					} else {
						Program.newline ();
						cout ("Unrecognized arguments or arguments not specified. Arguments may not contain spaces.");
						Program.newline ();
					}
					break;
				case ("cs"):
					args = input.Replace ("cs", "").Replace ("c s", "").Split (' ');
					if (args.Count () == 2) {
						Command.choosesimulation (Convert.ToInt32 (args [1]));
					} else {
						Program.newline ();
						cout ("Unrecognized arguments or arguments not specified. Arguments may not contain spaces.");
						Program.newline ();
					}
					break;
				case ("newsim"):
					Command.newsimulation ();
					break;
				case "ns":
					Command.newsimulation ();
					break;
				case "showsims":
					Command.showsimulations ();
					break;
				case "ss":
					Command.showsimulations ();
					break;
				case "help":
					Command.showhelp ();
					break;
				default:
					unrecognizedcommand ();
					break;
				}
			} else if (commands.Concat (shortcommands).Contains (input.Replace (" ", "").Replace ("help", ""))) {
				switch (input.Replace (" ", "")) {
					case ("helpstart"):
					Command.helpstart ();
					break;
					case ("helpstop"):
					Command.helpstop ();
					break;
				case ("helpshowfunctions"):
					Command.helpshowfunctions ();
					break;
				case ("helpsf"):
					Command.helpshowfunctions ();
					break;
				case ("helpcall"):
					Command.helpcall ();
					break;
				case ("helpremovesimulation"):
					Command.helpremovesimulation ();
					break;
				case ("helpremovesim"):
					Command.helpremovesimulation ();
					break;
				case ("helprs"):
					Command.helpremovesimulation ();
					break;
				case "helpupdatevariable":
					Command.helpupdatevariable ();
					break;
				case "helpupdatevar":
					Command.helpupdatevariable ();
					break;
				case "helpuv":
					Command.helpupdatevariable ();
					break;
				case "helpshowvariables":
					Command.helpshowvariables ();
					break;
				case "helpshowvars":
					Command.helpshowvariables ();
					break;
				case "helpsv":
					Command.helpshowvariables ();
					break;
				case "helpchoosesimulation":
					Command.helpchoosesimulation ();
					break;
				case "helpchoosesim":
					Command.helpchoosesimulation ();
					break;
				case "helpcs":
					Command.helpchoosesimulation ();
					break;
				case "helpnewsimulation":
					Command.helpnewsimulation ();
					break;
				case "helpnewsim":
					Command.helpnewsimulation ();
					break;
				case "helpns":
					Command.helpnewsimulation ();
					break;
				case "helpshowsimulations":
					Command.helpshowsimulations ();
					break;
				case "helpshowsims":
					Command.helpshowsimulations ();
					break;
				case "helpss":
					Command.helpshowsimulations ();
					break;
				case "helpquit":
					Command.helpquit ();
					break;
				case "helpq":
					Command.helpquit ();
					break;
				default:
					unrecognizedcommand ();
					break;
				}
			} else {
				unrecognizedcommand ();
			}
		}
		
		public static void cout (string text)
		{
			System.Console.WriteLine (text);
		}
		
		public static void unrecognizedcommand ()
		{
			newline ();
			cout ("Unrecognized command.");
			newline ();
		}
		
		public static void newline ()
		{
			System.Console.Write (Environment.NewLine);
		}

		public static string cin ()
		{
			return System.Console.ReadLine ();
		}
		
		public static void checkexit (string input)
		{
			if (exitstrings.Contains (input.Trim ().ToLower ())) {
				newline ();
				cout ("Exiting...");
				newline ();
				Environment.ExitCode = 0;
				Environment.Exit (0);
			}
		}
		
		private static void handleerror (Exception ex)
		{
			handleerror (ex.ToString ());
		}

		private static void handleerror (string error)
		{
			cout (Environment.NewLine + "Unexpected error occurred. Details: " + Environment.NewLine + error + Environment.NewLine);
		}
	}
}
